//
//  LBGaussianBlurFilter.mm
//  OpenGLGame
//
//  Created by Nguyen Cong Huy on 2/27/13.
//  Copyright (c) 2013 UIT. All rights reserved.
//

#include "LBGaussianBlurFilter.h"
#include "LBGraphics.h"
#include "LBProFilter.h"
#include "LBTwoPassTextureSamplingFilter.h"

char gaussianVerShaderStr[] = ""

"attribute vec4 position;"
"attribute vec2 texCoor;"

"uniform mat4 mWorld;"
"uniform mat4 mProjection;"

"uniform float texelWidthOffset; "
"uniform float texelHeightOffset; "

"const lowp int GAUSSIAN_SAMPLES = 9;"

"uniform highp float blurSize;"

"varying vec2 vtexCoor;"
"varying vec2 blurCoordinates[GAUSSIAN_SAMPLES];"

"void main()"
"{"
"    gl_Position = mProjection * mWorld * position;"
"    vtexCoor = texCoor;"

 "   int multiplier = 0;"
 "   highp vec2 blurStep;"
 "   highp vec2 singleStepOffset = vec2(texelHeightOffset, texelWidthOffset) * blurSize;"

"    for (lowp int i = 0; i < GAUSSIAN_SAMPLES; i++) {"
"        multiplier = (i - ((GAUSSIAN_SAMPLES - 1) / 2));"
        // Blur in x (horizontal)
"        blurStep = float(multiplier) * singleStepOffset;"
"        blurCoordinates[i] = texCoor.xy + blurStep;"
"    }"
"}";

char gaussianFragShaderStr[] = ""
"precision mediump float;"

"uniform sampler2D texture;"

"const lowp int GAUSSIAN_SAMPLES = 9;"

"varying vec2 vtexCoor;"
"varying vec2 blurCoordinates[GAUSSIAN_SAMPLES];"

"void main()"
"{"
"    lowp vec4 sum = vec4(0.0);"

"    sum += texture2D(texture, blurCoordinates[0]) * 0.05;"
"    sum += texture2D(texture, blurCoordinates[1]) * 0.09;"
"    sum += texture2D(texture, blurCoordinates[2]) * 0.12;"
"    sum += texture2D(texture, blurCoordinates[3]) * 0.15;"
"    sum += texture2D(texture, blurCoordinates[4]) * 0.18;"
"    sum += texture2D(texture, blurCoordinates[5]) * 0.15;"
"    sum += texture2D(texture, blurCoordinates[6]) * 0.12;"
"    sum += texture2D(texture, blurCoordinates[7]) * 0.09;"
"    sum += texture2D(texture, blurCoordinates[8]) * 0.05;"

"    gl_FragColor = sum;"

"}";


LBGaussianBlurFilter::LBGaussianBlurFilter(char* firstStageVertexShaderString, char* firstStageFragmentShaderString, char* secondStageVertexShaderString, char* secondStageFragmentShaderString){
    init(firstStageVertexShaderString, firstStageFragmentShaderString, secondStageVertexShaderString, secondStageFragmentShaderString);
}

LBGaussianBlurFilter::LBGaussianBlurFilter(){
    init(gaussianVerShaderStr, gaussianFragShaderStr, gaussianVerShaderStr, gaussianFragShaderStr);
}

void LBGaussianBlurFilter::setBlurSize(float blurSize){
    this->blurSize = blurSize;
    LBGraphics::getInstance()->useProgram(program);
    glUniform1f(horizontalBlurSizeUniform, blurSize);
    LBGraphics::getInstance()->useProgram(program2);
    glUniform1f(verticalBlurSizeUniform, blurSize);
}

void LBGaussianBlurFilter::init(char* firstStageVertexShaderString, char* firstStageFragmentShaderString, char* secondStageVertexShaderString, char* secondStageFragmentShaderString){
    LBTwoPassTextureSamplingFilter::init(firstStageVertexShaderString ? firstStageVertexShaderString : gaussianVerShaderStr,
         firstStageFragmentShaderString ? firstStageFragmentShaderString : gaussianFragShaderStr,
         secondStageVertexShaderString ? secondStageVertexShaderString : gaussianVerShaderStr,
         secondStageFragmentShaderString ? secondStageFragmentShaderString : gaussianFragShaderStr);
    
    LBGraphics::getInstance()->useProgram(program);
    horizontalBlurSizeUniform = program->uniformIndex("blurSize");
    LBGraphics::getInstance()->useProgram(program2);
    verticalBlurSizeUniform = program2->uniformIndex("blurSize");
    
    setBlurSize(1.0);
}

float LBGaussianBlurFilter::getBlurSize(){
    return blurSize;
}